﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Gmantis.Controls.DataGrid
{
    public class FilterDescription
    {
        // Methods
        public FilterDescription()
        {
        }

        public FilterDescription(string propertyName, DataGridFilterState state)
        {
            this.PropertyName = propertyName;
            this.State = state;
        }

        private Expression<Func<T, bool>> BuildExpression<T>(FilterDescription filterDescription)
        {
            ParameterExpression expression;
            return System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(this.BuildExpressionBody<T>(filterDescription, expression = Expression.Parameter(typeof(T), "i")), new ParameterExpression[] { expression });
        }

        internal System.Linq.Expressions.Expression BuildExpressionBody<T>(FilterDescription filterDescription, ParameterExpression param)
        {
            System.Linq.Expressions.Expression left = null;
            if ((filterDescription.State.FilterInfo != null) && (filterDescription.State.FilterInfo.Count > 0))
            {
                foreach (DataGridFilterInfo info in filterDescription.State.FilterInfo)
                {
                    System.Linq.Expressions.Expression right = null;
                    try
                    {
                        right = this.BuildOperationExpression<T>(param, filterDescription.PropertyName, info.FilterOperation, info.FilterType, info.Value, false);
                    }
                    catch (InvalidOperationException)
                    {
                        right = this.BuildOperationExpression<T>(param, filterDescription.PropertyName, info.FilterOperation, info.FilterType, info.Value, true);
                    }
                    if (left == null)
                    {
                        left = right;
                    }
                    else if (info.FilterCombination == DataGridFilterCombination.And)
                    {
                        left = System.Linq.Expressions.Expression.And(left, right);
                    }
                    else if (info.FilterCombination == DataGridFilterCombination.Or)
                    {
                        left = System.Linq.Expressions.Expression.Or(left, right);
                    }
                }
                return left;
            }
            return System.Linq.Expressions.Expression.Constant(true, typeof(bool));
        }

        private System.Linq.Expressions.Expression BuildOperationExpression<T>(ParameterExpression param, string propertyPath, DataGridFilterOperation filterOperation, DataGridFilterType filterType, object value, bool forceConvertion)
        {
            System.Linq.Expressions.Expression expression = System.Linq.Expressions.Expression.Constant(false, typeof(bool));
            System.Linq.Expressions.Expression nullCheck = System.Linq.Expressions.Expression.Constant(true, typeof(bool));
            System.Linq.Expressions.Expression expression3 = PropertyPathHelper.MakePropertyAccess(typeof(T), param, propertyPath, ref nullCheck);
            Type type = (value == null) ? ((expression3.Type.IsNullableType() || expression3.Type.IsClass) ? expression3.Type : typeof(object)) : value.GetType();
            System.Linq.Expressions.Expression expression4 = System.Linq.Expressions.Expression.Constant(value, type);
            if (forceConvertion)
            {
                expression3 = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Call(typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type), typeof(IFormatProvider) }), new Expression[] { expression3, Expression.Constant(expression4.Type, typeof(Type)), Expression.Constant(null, typeof(IFormatProvider)) }), expression4.Type);
            }
            switch (filterType)
            {
                case DataGridFilterType.CheckBox:
                case DataGridFilterType.Other:
                    if (expression3.Type != expression4.Type)
                    {
                        if (!expression3.Type.IsNullableType() || (expression3.Type.GetNonNullableType() != expression4.Type))
                        {
                            if (filterType == DataGridFilterType.CheckBox)
                            {
                                if (expression3.Type != typeof(bool?))
                                {
                                    expression3 = System.Linq.Expressions.Expression.Convert(expression3, typeof(bool?));
                                }
                                if (expression4.Type != typeof(bool?))
                                {
                                    expression4 = System.Linq.Expressions.Expression.Convert(expression4, typeof(bool?));
                                }
                            }
                            else if (expression3.Type.IsAssignableFrom(expression4.Type))
                            {
                                expression3 = System.Linq.Expressions.Expression.Convert(expression3, expression4.Type);
                            }
                        }
                        else
                        {
                            expression4 = System.Linq.Expressions.Expression.Convert(expression4, expression3.Type);
                        }
                    }
                    if (filterOperation == DataGridFilterOperation.Equal)
                    {
                        if (expression3.Type == expression4.Type)
                        {
                            expression = System.Linq.Expressions.Expression.Equal(expression3, expression4);
                        }
                        else
                        {
                            expression = System.Linq.Expressions.Expression.Constant(false);
                        }
                    }
                    else if (filterOperation == DataGridFilterOperation.NotEqual)
                    {
                        if (expression3.Type == expression4.Type)
                        {
                            expression = System.Linq.Expressions.Expression.NotEqual(expression3, expression4);
                        }
                        else
                        {
                            expression = System.Linq.Expressions.Expression.Constant(true);
                        }
                    }
                    goto Label_068F;

                case DataGridFilterType.MultiValue:
                    if (filterOperation != DataGridFilterOperation.All)
                    {
                        if (filterOperation == DataGridFilterOperation.IsOneOf)
                        {
                            IEnumerable<object> enumerable = ((IEnumerable)value).Cast<object>().ToArray<object>();
                            MethodInfo method = base.GetType().GetMethod("IsOneOf", BindingFlags.NonPublic | BindingFlags.Static);
                            if (expression3.Type != typeof(object))
                            {
                                expression3 = System.Linq.Expressions.Expression.Convert(expression3, typeof(object));
                            }
                            expression = System.Linq.Expressions.Expression.Call(method, System.Linq.Expressions.Expression.Constant(enumerable), expression3);
                        }
                        else if ((filterOperation != DataGridFilterOperation.None) && (filterOperation == DataGridFilterOperation.Some))
                        {
                        }
                    }
                    else
                    {
                        expression = System.Linq.Expressions.Expression.Constant(true, typeof(bool));
                    }
                    goto Label_068F;

                case DataGridFilterType.DateTime:
                case DataGridFilterType.Numeric:
                    if ((((filterOperation == DataGridFilterOperation.Equal) || (filterOperation == DataGridFilterOperation.NotEqual)) || ((filterOperation == DataGridFilterOperation.GreaterThan) || (filterOperation == DataGridFilterOperation.GreaterThanOrEqual))) || ((filterOperation == DataGridFilterOperation.LessThan) || (filterOperation == DataGridFilterOperation.LessThanOrEqual)))
                    {
                        if (filterType != DataGridFilterType.Numeric)
                        {
                            if (expression3.Type.GetNonNullableType() != typeof(DateTime))
                            {
                                nullCheck = System.Linq.Expressions.Expression.AndAlso(nullCheck, System.Linq.Expressions.Expression.NotEqual(expression3, System.Linq.Expressions.Expression.Constant(null)));
                                expression3 = PropertyPathHelper.Convert(expression3, expression3.Type.IsNullableType() ? typeof(DateTime?) : typeof(DateTime));
                            }
                        }
                        else if (!expression3.Type.GetNonNullableType().IsNumeric())
                        {
                            nullCheck = System.Linq.Expressions.Expression.AndAlso(nullCheck, System.Linq.Expressions.Expression.NotEqual(expression3, System.Linq.Expressions.Expression.Constant(null)));
                            expression3 = PropertyPathHelper.Convert(expression3, expression3.Type.IsNullableType() ? typeof(double?) : typeof(double));
                        }
                        if (type != expression3.Type)
                        {
                            if (type.GetNonNullableType() != expression3.Type.GetNonNullableType())
                            {
                                value = Convert.ChangeType(value, expression3.Type.GetNonNullableType(), CultureInfo.InvariantCulture);
                            }
                            expression4 = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value), expression3.Type);
                        }
                        if (filterOperation == DataGridFilterOperation.Equal)
                        {
                            expression = System.Linq.Expressions.Expression.Equal(expression3, expression4);
                        }
                        else if (filterOperation == DataGridFilterOperation.NotEqual)
                        {
                            expression = System.Linq.Expressions.Expression.NotEqual(expression3, expression4);
                        }
                        else if (filterOperation == DataGridFilterOperation.GreaterThan)
                        {
                            expression = System.Linq.Expressions.Expression.GreaterThan(expression3, expression4);
                        }
                        else if (filterOperation == DataGridFilterOperation.GreaterThanOrEqual)
                        {
                            expression = System.Linq.Expressions.Expression.GreaterThanOrEqual(expression3, expression4);
                        }
                        else if (filterOperation == DataGridFilterOperation.LessThan)
                        {
                            expression = System.Linq.Expressions.Expression.LessThan(expression3, expression4);
                        }
                        else if (filterOperation == DataGridFilterOperation.LessThanOrEqual)
                        {
                            expression = System.Linq.Expressions.Expression.LessThanOrEqual(expression3, expression4);
                        }
                    }
                    goto Label_068F;

                case DataGridFilterType.Text:
                    {
                        if (expression3.Type.Equals(typeof(string)))
                        {
                            expression3 = System.Linq.Expressions.Expression.Condition(System.Linq.Expressions.Expression.Equal(expression3, System.Linq.Expressions.Expression.Constant(null)), System.Linq.Expressions.Expression.Constant(string.Empty), expression3);
                            break;
                        }
                        MethodCallExpression ifFalse = System.Linq.Expressions.Expression.Call(expression3, typeof(object).GetMethod("ToString", new Type[0]), (System.Linq.Expressions.Expression[])null);
                        if (!expression3.Type.IsValueType)
                        {
                            expression3 = System.Linq.Expressions.Expression.Condition(System.Linq.Expressions.Expression.Equal(expression3, System.Linq.Expressions.Expression.Constant(null)), System.Linq.Expressions.Expression.Constant(string.Empty), ifFalse);
                            break;
                        }
                        expression3 = ifFalse;
                        break;
                    }
                default:
                    goto Label_068F;
            }
            if ((filterOperation == DataGridFilterOperation.IsEmpty) || (filterOperation == DataGridFilterOperation.IsNotEmpty))
            {
                expression = System.Linq.Expressions.Expression.Equal(expression3, System.Linq.Expressions.Expression.Constant(string.Empty, typeof(string)));
                if (filterOperation == DataGridFilterOperation.IsNotEmpty)
                {
                    expression = System.Linq.Expressions.Expression.Not(expression);
                }
            }
            else
            {
                expression4 = System.Linq.Expressions.Expression.Constant(((value as string) ?? value.ToString()).ToUpper(), typeof(string));
                expression3 = System.Linq.Expressions.Expression.Call(expression3, typeof(string).GetMethod("ToUpper", new Type[0]), (System.Linq.Expressions.Expression[])null);
                if (((filterOperation == DataGridFilterOperation.Contains) || (filterOperation == DataGridFilterOperation.StartsWith)) || (filterOperation == DataGridFilterOperation.EndsWith))
                {
                    expression = System.Linq.Expressions.Expression.Call(expression3, typeof(string).GetMethod(filterOperation.ToString(), new Type[] { typeof(string) }), new System.Linq.Expressions.Expression[] { expression4 });
                }
                else if (filterOperation == DataGridFilterOperation.Equal)
                {
                    MethodInfo info2 = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
                    expression = System.Linq.Expressions.Expression.Call(expression3, info2, new System.Linq.Expressions.Expression[] { expression4 });
                }
                else if (filterOperation == DataGridFilterOperation.NotEqual)
                {
                    MethodInfo info3 = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
                    expression = System.Linq.Expressions.Expression.Not(System.Linq.Expressions.Expression.Call(expression3, info3, new System.Linq.Expressions.Expression[] { expression4 }));
                }
            }
        Label_068F:
            return System.Linq.Expressions.Expression.AndAlso(nullCheck, expression);
        }

        public Expression<Func<T, bool>> GetExpression<T>()
        {
            return this.BuildExpression<T>(this);
        }

        public Func<T, bool> GetPredicate<T>()
        {
            Expression<Func<T, bool>> expression = this.GetExpression<T>();
            if (expression != null)
            {
                return expression.Compile();
            }
            return null;
        }

        private static bool IsOneOf(object[] array, object elem)
        {
            return array.Any<object>(item => (((item == null) && (elem == null)) || ((item != null) && item.Equals(elem))));
        }

        // Properties
        public string PropertyName { get; set; }

        public DataGridFilterState State { get; set; }
    }
}
